﻿using System;
using System.Windows;
using System.Collections.Generic;
using System.Windows.Input;
using System.Windows.Controls;

namespace Shrestha.Trigraph
{
    /// <summary>
    /// Interaction logic for MainWindow.xaml
    /// </summary>
    public partial class MainWindow: IObservable<WorkingSet>
    {
        private bool canvasLoaded = false;
        private WorkingSet working, maximal;
        private bool dataLoaded = false;

        public WorkingSet MaximalSet
        {
            get { return maximal; }
        }

        private List<IObserver<WorkingSet>> observers;

        public MainWindow()
        {
            InitializeComponent();
            observers = new List<IObserver<WorkingSet>>();

            tgc.setParent(this);
            //tgc.Subscribe(this);
        }

        private bool LoadTriGraphCanvas()
        {
            try
            {
                tgc.Draw(working);
                tgc.Subscribe(this);
            }
            catch (Exception e)
            {
                Console.WriteLine(e.Message);
                return false;
            }
            return true;
        }

        private void MenuItem_Map_Click(object sender, RoutedEventArgs e)
        {
            MapDisplay mapd = new MapDisplay(working, this);
            mapd.Show();
            mapd.Subscribe(this);
        }

        private void MenuItem_Exit_Click(object sender, RoutedEventArgs e)
        {
            Environment.Exit(0);
        }

        private void MenuItem_LoadDataSource_Click(object sender, RoutedEventArgs e)
        {
            DataSourceLoader dsLoader = new DataSourceLoader();
            if ((bool)dsLoader.ShowDialog())
            {
                maximal = new WorkingSet();
                maximal = dsLoader.CurrentSet;

                working = dsLoader.CurrentSet;

                canvasLoaded = LoadTriGraphCanvas();

                this.dateFrom.SelectedDate = working.MinDbDate;
                this.dateTo.SelectedDate = working.MaxDbDate;

                this.latFrom.Text = working.MinDbLat.ToString();
                this.latTo.Text = working.MaxDbLat.ToString();

                this.lngFrom.Text = working.MinDbLng.ToString();
                this.lngTo.Text = working.MaxDbLng.ToString();

                this.btnDrawGraph.IsEnabled =
                    this.btnResetGraph.IsEnabled = 
                    this.dateTo.IsEnabled = 
                    this.dateFrom.IsEnabled = 
                    this.latFrom.IsEnabled =
                    this.latTo.IsEnabled =
                    this.lngFrom.IsEnabled =
                    this.lngTo.IsEnabled = 
                    this.dataLoaded = 
                    true;
            } 
        }

        private void btnDrawGraph_Click(object sender, RoutedEventArgs e)
        {
            try
            {
                working = Filter.GetFiltered(double.Parse(latFrom.Text), double.Parse(latTo.Text), double.Parse(lngFrom.Text), double.Parse(lngTo.Text), Filter.GetFiltered(dateFrom.SelectedDate.Value, dateTo.SelectedDate.Value, maximal));
            }
            catch (Exception exp)
            {
                MessageBox.Show("Error: Please enter valid range for the filters");
                Console.WriteLine(exp.Message);
            }
            tgc.Draw(working);
        }

        private void btnResetGraph_Click(object sender, RoutedEventArgs e)
        {
            dateFrom.DisplayDate = MaximalSet.MinDbDate;
            dateTo.DisplayDate = MaximalSet.MaxDbDate;
            latFrom.Text = MaximalSet.MinDbLat.ToString();
            latTo.Text = MaximalSet.MaxDbLat.ToString();
            lngFrom.Text = MaximalSet.MinDbLng.ToString();
            lngTo.Text = MaximalSet.MaxDbLng.ToString();
            tgc.Draw(maximal);
            
        }

        private void dateFrom_SelectedDateChanged(object sender, System.Windows.Controls.SelectionChangedEventArgs e)
        {
            dateTo.DisplayDateStart = dateFrom.SelectedDate;
        }

        private void Window_SizeChanged(object sender, SizeChangedEventArgs e)
        {
            if(canvasLoaded) LoadTriGraphCanvas();
        }

        public void UpdateWorkingSets(WorkingSet ws)
        {
            foreach (var observer in observers)
            {
                observer.OnNext(ws);
            }
        }

        public bool isSame(WorkingSet ws)
        {
            if (ws.MinDbLat == working.MinDbLat &&
                ws.MinDbLng == working.MinDbLng &&
                ws.MaxDbLat == working.MaxDbLat &&
                ws.MaxDbLng == working.MaxDbLng &&
                ws.MinDbDate == working.MinDbDate &&
                ws.MaxDbDate == working.MaxDbDate)
                return true;
            else return false;
        }

        public IDisposable Subscribe(IObserver<WorkingSet> observer)
        {
            if (!observers.Contains(observer))
            {
                observers.Add(observer);
            }
            return new Unsubscriber(observers, observer);
        }

        private class Unsubscriber : IDisposable
        {
            private List<IObserver<WorkingSet>> _observers;
            private IObserver<WorkingSet> _observer;

            public Unsubscriber(List<IObserver<WorkingSet>> observers, IObserver<WorkingSet> observer)
            {
                this._observers = observers;
                this._observer = observer;
            }

            public void Dispose()
            {
                if (_observer != null && _observers.Contains(_observer))
                    _observers.Remove(_observer);
            }
        }

        protected void OnPreviewTextInput(object sender, System.Windows.Input.TextCompositionEventArgs e)
        {
            e.Handled = !AreAllValidNumericChars(e.Text);
            base.OnPreviewTextInput(e);
        }

        private bool AreAllValidNumericChars(string str)
        {
            foreach (char c in str)
            {
                if (!Char.IsNumber(c)) return false;
            }

            return true;
        }

        bool mouseDown = false; // Set to 'true' when mouse is held down.
        Point mouseDownPos; // The point where the mouse button was clicked down.
        bool beingDragged = false;

        private void Selection_MouseDown(object sender, MouseButtonEventArgs e)
        {
            // Capture and track the mouse.
            Mouse.OverrideCursor = Cursors.Cross;
            mouseDown = true;
            mouseDownPos = e.GetPosition(tgc);
            tgc.CaptureMouse();

            // Initial placement of the drag selection box.         
            Canvas.SetLeft(selectionBox, mouseDownPos.X);
            Canvas.SetTop(selectionBox, mouseDownPos.Y);
            selectionBox.Width = 0;
            selectionBox.Height = 0;

            // Make the drag selection box visible.
            selectionBox.Visibility = Visibility.Visible;
        }

        private void Selection_MouseUp(object sender, MouseButtonEventArgs e)
        {
            // Release the mouse capture and stop tracking it.
            mouseDown = false;
            tgc.ReleaseMouseCapture();

            // Hide the drag selection box.
            selectionBox.Visibility = Visibility.Collapsed;

            Point mouseUpPos = e.GetPosition(tgc);

            Mouse.OverrideCursor = null;
            if (beingDragged && dataLoaded)
            {
                double minFilteredLat, maxFilteredLat, minFilteredLng, maxFilteredLng;
                DateTime maxFilteredTime, minFilteredTime;
                tgc.GetExtents(mouseDownPos, mouseUpPos, out minFilteredLat, out maxFilteredLat, out minFilteredLng, out maxFilteredLng, out maxFilteredTime, out minFilteredTime );
                //working = Filter.GetFiltered(minFilteredLat, maxFilteredLat, minFilteredLng, maxFilteredLng, maximal);
                working = Filter.GetFiltered(minFilteredTime, maxFilteredTime, maximal);
                if (working.TotalRows != 0) tgc.Draw(working);
                beingDragged = false;
            }
            
        }

        private void Selection_MouseMove(object sender, MouseEventArgs e)
        {
            if (mouseDown)
            {
                // When the mouse is held down, reposition the drag selection box.

                Point mousePos = e.GetPosition(tgc);

                if (mouseDownPos.X < mousePos.X)
                {
                    Canvas.SetLeft(selectionBox, mouseDownPos.X);
                    selectionBox.Width = mousePos.X - mouseDownPos.X;
                }
                else
                {
                    Canvas.SetLeft(selectionBox, mousePos.X);
                    selectionBox.Width = mouseDownPos.X - mousePos.X;
                }

                if (mouseDownPos.Y < mousePos.Y)
                {
                    Canvas.SetTop(selectionBox, mouseDownPos.Y);
                    selectionBox.Height = mousePos.Y - mouseDownPos.Y;
                }
                else
                {
                    Canvas.SetTop(selectionBox, mousePos.Y);
                    selectionBox.Height = mouseDownPos.Y - mousePos.Y;
                }
                if (mouseDownPos.Equals(mousePos))
                    beingDragged = false;
                else
                    beingDragged = true;
            }
        }


        
    }
}
